{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Cross-Resonance Gate\n",
    "\n",
    "*Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Outline\n",
    "\n",
    "This tutorial introduces how to generate optimized pulses for Cross-Resonance (CR) gate using Quanlse. Unlike the iSWAP and CZ gate implementation in previous tutorials, CR gate is implemented using an all-microwave drive. The outline of this tutorial is as follows:\n",
    "- Introduction\n",
    "- Preparation\n",
    "- Construct Hamiltonian\n",
    "- Generate and optimize pulses via Quanlse Cloud Service\n",
    "- Summary"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction\n",
    "\n",
    "**Fundamentals**\n",
    "\n",
    "Unlike some of the other gates we have seen before, the Cross-Resonance (CR) gate only uses microwaves to implement the two-qubit interaction such that we could avoid noise due to magnetic flux. The physical realization of the CR gate includes two coupled qubits with fixed frequencies. This can be done by driving the control qubit at the frequency of the target qubit. This is shown in the figure below:\n",
    "\n",
    "\n",
    "![cr-circuit](figures/cr-circuit.png)\n",
    "\n",
    "\n",
    "\n",
    "We will first look at the effective Hamiltonian of the system (for details, please refer to Ref. \\[1\\] ). In the doubly rotating frame, the effective Hamiltonian for cross-resonance effect in terms of the drive strength $A$, detuning $\\Delta$, drive phase $\\phi_0$, and coupling strength $g_{01}$ is given \\[1\\] (for simplicity, we choose $\\hbar = 1$) :\n",
    "\n",
    "\n",
    "$$\n",
    "\\hat{H}_{\\rm eff} = \\frac{A}{4\\Delta}g_{01}(\\cos{\\phi_0}\\hat{\\sigma}_0^z\\hat{\\sigma}_1^x+\\sin{\\phi_0}\\hat{\\sigma}_0^z\\hat{\\sigma}_1^y).\n",
    "$$\n",
    "\n",
    "When $\\phi_0=0$, the cross-resonance effect allows for effective coupling of $\\hat{\\sigma}^z_0\\otimes\\hat{\\sigma}_1^x$. We can thus derive the time evolution matrix from the effective Hamiltonian above:\n",
    "\n",
    "$$\n",
    "U_{\\rm CR}(\\theta)=e^{-i\\frac{\\theta}{2}\\hat{\\sigma}^z_0\\otimes\\hat{\\sigma}^x_1},\n",
    "$$\n",
    "\n",
    "where $\\theta = \\Omega_0 gt/(2\\Delta)$ ($t$ is the gate time). We can see that the cross-resonance effect enables a conditional rotation on qubit 1 (target qubit) depending on the state of qubit 0 (control qubit).   \n",
    "\n",
    "\n",
    "Following the derivation above, the matrix form of the CR gate is (refer to \\[2\\] for more details):\n",
    "$$\n",
    "U_{\\rm CR}(\\theta) = \\begin{bmatrix} \n",
    "\\cos{\\frac{\\theta}{2}} & -i\\sin{\\frac{\\theta}{2}} & 0 & 0 \\\\\n",
    "-i\\sin{\\frac{\\theta}{2}} & \\cos{\\frac{\\theta}{2}} & 0 & 0 \\\\ \n",
    "0 & 0 & \\cos{\\frac{\\theta}{2}} & i\\sin{\\frac{\\theta}{2}} \\\\\n",
    "0 & 0 & i\\sin{\\frac{\\theta}{2}} & \\cos{\\frac{\\theta}{2}} \n",
    "\\end{bmatrix}.\n",
    "$$\n",
    "\n",
    "\n",
    "In particular, the matrix representation of a CR gate with $\\theta = -\\frac{\\pi}{2}$ is:\n",
    "\n",
    "$$\n",
    "U_{\\rm CR}(-\\pi/2) = \\frac{\\sqrt{2}}{2} \n",
    "\\begin{bmatrix}\n",
    "1 & i & 0 & 0 \\\\\n",
    "i & 1 & 0 & 0 \\\\\n",
    "0 & 0 & 1 & -i \\\\\n",
    "0 & 0 & -i & 1\n",
    "\\end{bmatrix}.\n",
    "$$\n",
    "\n",
    "**Application**\n",
    "\n",
    "Having analyzed some of the fundamentals of the CR gate, we now switch our focus to the applications of the CR gate in quantum computing - one of which is the implementation of a CNOT gate with a CR gate and two additional single-qubit gates.\n",
    "\n",
    "![cr-gate](figures/cr-gate.png) \n",
    "\n",
    "In this tutorial, we will model the system consisting of two three-level qubits and apply the drive pulse to the control qubit (qubit $q_0$) at the frequency of the target qubit (qubit $q_1$). By performing a rotating wave approximation (RWA), the Hamiltonian can be expressed as (refer to \\[1\\] for more details):\n",
    "\n",
    "$$\n",
    "\\hat{H}_{\\rm sys} = (\\omega_{\\rm q0}-\\omega_{\\rm d})\\hat{a}_{0}^{\\dagger }\\hat{a}_0 + (\\omega_{\\rm q1}-\\omega_{\\rm d})\\hat{a}_1^\\dagger \\hat{a}_1 + \\frac{\\alpha_0}{2} \\hat{a}^{\\dagger2}_0\\hat{a}^2_0 + \\frac{\\alpha_1}{2} \\hat{a}^{\\dagger2}_1\\hat{a}^2_1+\\frac{g}{2}(\\hat{a}_0\\hat{a}_1^\\dagger + \\hat{a}_0^\\dagger\\hat{a}_1) + \\Omega_0^x(t)\\frac{\\hat{a}^\\dagger_0+\\hat{a}_0}{2}.\n",
    "$$\n",
    "\n",
    "Please refer to the following chart for symbols' definitions:\n",
    "\n",
    "| Notation | Definition |\n",
    "|:--------:|:----------:|\n",
    "|$\\omega_{\\rm qi}$ | qubit $q_i$'s frequency |\n",
    "|$\\omega_{\\rm d}$|drive frequency|\n",
    "|$\\hat{a}_i^{\\dagger}$ | creation operator |\n",
    "|$\\hat{a}_i$ | annihilation operator |\n",
    "|$\\alpha_i$| qubit $q_i$'s anharmonicity |\n",
    "| $g$ | coupling strength |\n",
    "| $\\Omega_0^x$(t) | pulse on the x channel |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preparation\n",
    "\n",
    "After you have successfully installed Quanlse, you could run the Quanlse program below following this tutorial. To run this particular tutorial, you would need to import the following packages from Quanlse and other commonly-used Python libraries:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import Hamiltonian-related module\n",
    "from Quanlse.Utils import Hamiltonian as qham\n",
    "from Quanlse.Utils.Operator import driveX, number, duff\n",
    "\n",
    "# Import optimizer for the cross-resonance gate\n",
    "from Quanlse.remoteOptimizer import remoteOptimizeCr\n",
    "\n",
    "# Import tools to analyze the result\n",
    "from Quanlse.Utils.Tools import project, unitaryInfidelity\n",
    "\n",
    "# Import numpy and math\n",
    "from numpy import round\n",
    "from math import pi"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Construct Hamiltonian\n",
    "\n",
    "Now, we need to construct the above Hamiltonian using Quanlse. In Quanlse, all information regarding a Hamiltonian is stored in a dictionary. We start by defining some of the basic parameters needed for constructing a Hamiltonian dictionary: the sampling period, the number of qubits in the system, and the system's energy levels to consider. To initialize our Hamiltonian dictionary, we call the function `createHam()` from the module `Hamiltonian`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Sampling period\n",
    "dt = 2.0\n",
    "\n",
    "# Number of qubits\n",
    "qubits = 2\n",
    "\n",
    "# System energy level\n",
    "level = 3\n",
    "\n",
    "# Initialize the Hamiltonian\n",
    "ham = qham.createHam(title='cr-gate', dt=dt, qubitNum=qubits, sysLevel=level)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can start constructing our Hamiltonian. Before we start, we would need to define a few constants to pass in as the function's arguments:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Parameters setting  \n",
    "g = 0.0038 * (2 * pi) # Coupling strength 1\n",
    "wq0  = 4.914 * (2 * pi) # Transition frequency for qubit 0, GHz\n",
    "wq1 = 4.714 * (2 * pi) # Transition frequency for qubit 1, GHz\n",
    "wd = wq1 # Drive frequency is the frequency for qubit 1, GHz \n",
    "anharm0 = -0.33 * (2 * pi) # Anharmonicity for qubit 0, GHz \n",
    "anharm1 = -0.33 * (2 * pi) # Anharmonicity for qubit 1, GHz \n",
    "detuning0 = wq0 - wd\n",
    "detuning1 = wq1 - wd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we need to add the following terms to the Hamiltonian dictionary we initilized earlier:\n",
    "$$\n",
    "\\hat{H}_{\\rm drift} = (\\omega_{\\rm q0}-\\omega_{\\rm d}) \\hat{a}_0^\\dagger \\hat{a}_0 + (\\omega_{\\rm q1}-\\omega_{\\rm d}) \\hat{a}_1^\\dagger \\hat{a}_1 + \\frac{\\alpha_0}{2} \\hat{a}_0^{\\dagger}\\hat{a}_0^{\\dagger}\\hat{a}_0 \\hat{a}_0 + \\frac{\\alpha_1}{2} \\hat{a}_1^{\\dagger}\\hat{a}_1^{\\dagger}\\hat{a}_1 \\hat{a}_1,\n",
    "$$\n",
    "$$\n",
    "\\hat{H}_{\\rm coup} = \\frac{g_{01}}{2}(\\hat{a}_0 \\hat{a}_1^\\dagger+\\hat{a}^\\dagger_0 \\hat{a}_1).\n",
    "$$\n",
    "\n",
    "In Quanlse's `Operator` module, we have provided tools that would allow the users to construct the commonly used operators quickly. The detuning term $(\\omega_{\\rm q}-\\omega_{\\rm d})\\hat{a}^\\dagger \\hat{a}$ and the anharmonicity term $\\frac{\\alpha}{2}\\hat{a}^\\dagger\\hat{a}^\\dagger \\hat{a} \\hat{a} $ can be respectively generated using  `number(n)` and `duff(n)` from the `Operator` module: the two functions `number(n)` and `duff(n)` return the $n \\times n$ matrices for number operators and duffing operators. The coupling term, which takes the form, $\\frac{g}{2}(\\hat{a}_i^\\dagger\\hat{a}_j+\\hat{a}_i\\hat{a}_j^\\dagger$), can be directly added to the Hamiltonian using function `addCoupling()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Add the detuning terms\n",
    "qham.addDrift(ham, name='detuning0', onQubits=0, amp=detuning0, matrices=number(level))\n",
    "qham.addDrift(ham, name='detuning1', onQubits=1, amp=detuning1, matrices=number(level))\n",
    "\n",
    "# Add the anharmonicity terms\n",
    "qham.addDrift(ham, name='anharm0', onQubits=0, amp=0.5 * anharm0, matrices=duff(level))\n",
    "qham.addDrift(ham, name='anharm1', onQubits=1, amp=0.5 * anharm1, matrices=duff(level))\n",
    "\n",
    "# Add the coupling term\n",
    "qham.addCoupling(ham, 'coupling', [0, 1], g=0.5 * g)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, we add the control term to the system Hamiltonian. The matrix for the control term can be defined by `driveX(level)` in the `Operator` module. \n",
    "$$\n",
    "\\hat{H}_{\\rm ctrl} = \\Omega_0^x(t)\\frac{\\hat{a}^\\dagger_0+\\hat{a}_0}{2}.\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Add the control term\n",
    "qham.addControl(ham, name='q0-ctrlx', onQubits=0, matrices=driveX(level))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With the system Hamiltonian built, we can now move on to the optimization."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Generate and optimize pulse via Quanlse Cloud Service"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The optimization process usually takes a long time to process on local devices; however, we provide a cloud service that could speed up this process significantly. To use the Quanlse Cloud Service, the users need to acquire a token from http://quantum-hub.baidu.com."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import tools to get access to cloud service\n",
    "from Quanlse import Define\n",
    "\n",
    "# To use remoteOptimizerCr on cloud, paste your token (a string) here\n",
    "Define.hubToken = ''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To find the optimized pulse for CR gate, we use the function `remoteOptimizeCr()`, which takes the Hamiltonian we had previously defined, amplitude's bound, gate time, maximum iterations, and target infidelity. By calling `remoteOptimizeCr()`, the user can submit the optimization task to the Quanlse's server. If the user wants to further mitigate the infidelity, we encourage trying an increased gate time `tg` (the duration of a CR gate is around 200 to 400 nanoseconds). Users can also try increasing the search space by setting larger `aBound` and `maxIter`.\n",
    "\n",
    "The gate infidelity for performance assessment throughout this tutorial is defined as ${\\rm infid} = 1 - \\frac{1}{d}\\left|{\\rm Tr}[U^\\dagger_{\\rm goal}P(U)]\\right|$, where $U_{\\rm goal}$ is exactly the target unitary transformation $U_{\\rm CR}(-\\pi/2)$; $d$ is the dimension of $U_{\\rm goal}$; and $U$ is the unitary evolution of the three-level system defined previously. Note that $P(U)$ in particular describes the evolution projected to the computational subspace."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set amplitude bound\n",
    "aBound = (1.0, 3.0)\n",
    "\n",
    "# Run the optimization\n",
    "ham, infidelity = remoteOptimizeCr(ham, aBound=aBound, tg=200, maxIter=5, targetInfidelity=0.005)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can visualize the generated pulse using `plotWaves()`. (details regarding `plotWaves()` are covered in [single-qubit-gate.ipynb](https://quanlse.baidu.com/#/doc/tutorial-single-qubit))  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# Print waves and the infidelity\n",
    "qham.plotWaves(ham, ['q0-ctrlx'])\n",
    "print(f'infidelity: {infidelity}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The users can also print the the projected evolution $P(U)$ using the following lines:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Print the projected evolution\n",
    "result = qham.simulate(ham)\n",
    "process2d = project(result[\"unitary\"], qubits, level, 2)\n",
    "print(\"The projected evolution P(U):\\n\", round(process2d, 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Moreover, for those interested in acquiring the numerical data of the generated pulse for each `dt`, use function `getPulseSequences()`, which takes a Hamiltonian dictionary and channels' names as parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "qham.getPulseSequences(ham, 'q0-ctrlx')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "From constructing the system Hamiltonian to generating an optimized pulse on Quanlse Cloud Service, we have successfully devised a pulse to implement a cross-resonace gate with high fidelity. The users are encouraged to try parameter values different from this tutorial to obtain the optimal result."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "source": [
    "## References \n",
    "\n",
    "\\[1\\] [Rigetti, Chad, and Michel Devoret. \"Fully microwave-tunable universal gates in superconducting qubits with linear couplings and fixed transition frequencies.\" *Physical Review B* 81.13 (2010): 134507.](https://qulab.eng.yale.edu/documents/papers/Rigetti,%20Devoret%20-%20Fully%20Microwave-Tunable%20Universal%20Gates%20in%20Superconducting%20Qubits%20with%20Linear%20Couplings%20and%20Fixed%20Transition%20Frequencies.pdf)\n",
    "\n",
    "\\[2\\] Nielsen, Michael A., and Isaac L. Chuang. Quantum Computation and Quantum Information: 10th Anniversary Edition. Cambridge University Press, 2010."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}